
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>K8s 为什么要弃用 Docker？</title>
            </head>
            <body>
            <a href="https://andyoung.blog.csdn.net">原作者博客</a>
            <div id="content_views" class="markdown_views prism-atom-one-light">
                    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
                        <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
                    </svg>
                    <p>在写“K8s”系列文章的过程中，很多读者留言询问 K8s 弃用 Docker 的事，担心现在学习 Docker 是否还值得，是不是该切换到 containerd 或其他运行时。</p> 
<p>这些怀疑有一定的道理。两年前，K8s 发布“弃用 Docker”的消息时，确实在社区引起了“轩然大波”，影响甚至蔓延到了社区之外，K8s 不得不写了好几篇博客来重复解释原因。</p> 
<p>两年过去了，虽然 K8s 1.24 已经实现了“弃用 Docker”的目标，但很多人似乎对这一点还不是很清楚。所以本篇文章就来聊聊这个话题。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/36dde07d1625301a2b06806e37094556.png" alt="图片"></p> 
<h3><a id="CRI_8"></a>CRI（容器运行时接口）</h3> 
<p>要理解 K8s 为何“弃用 Docker”，我们得回顾一下 K8s 的发展史。</p> 
<p>2014 年，Docker 正处于鼎盛时期，而 K8s 刚刚诞生。虽然它得到了 Google 和 Borg 的支持，但它还是比较新的。</p> 
<p>因此，K8s 首先选择支持 Docker 。</p> 
<p>快进到 2016 年，CNCF 成立一年，K8s 也发布了 1.0 版本，可以正式用于生产环境。这些都表明 K8s 已经长大了。</p> 
<p>于是宣布加入 CNCF，成为第一个 CNCF 托管项目。它想利用基金会的力量联合其他厂商来“打倒”Docker。</p> 
<p>在 2016 年底的 1.5 版本中，K8s 引入了新的接口标准：CRI：Container Runtime Interface 容器运行时接口。</p> 
<p>CRI 使用<code>ProtoBuffer</code>and<code>gPRC</code>来指定<code>kubelet</code>应该如何调用容器运行时来管理容器和镜像，但这是一组与以前的 Docker 调用完全不兼容的新接口。</p> 
<p>显然它不想再和 Docker 绑定，在底层允许访问其他容器技术（如 rkt、kata 等），可以随时“踢开” Docker。</p> 
<p>但此时 Docker 已经非常成熟，市场的惯性也非常强。各大云厂商不可能一下子全部替换掉 Docker。</p> 
<p>因此，K8s 只能同时提供一种“折中”的方案，在<code>kubelet</code>和 Docker 之间增加一个“适配器”，将 Docker 的接口转换为 CRI 兼容的接口：</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/9f8acfba0005449ed381d7c73725193b.png" alt="图片"></p> 
<p>因为这个“适配器”夹在<code>kubelet</code>Docker 和 Docker 之间，所以形象地称为“shim”，意思是“垫片”。</p> 
<p>有了 CRI 和 shim，虽然 K8s 仍然使用 Docker 作为底层运行时，但它也具备了与 Docker 解耦的条件，从而拉开了“弃用 Docker”大戏的帷幕。</p> 
<h3><a id="Containerd_36"></a>Containerd</h3> 
<p>面对挑战，Docker 采取了“断臂求生”的策略，推动自身重构，将原有单一架构的 Docker Engine 拆分成多个模块，其中 Docker daemon 部分捐赠给 CNCF，<code>containerd</code> 形成。</p> 
<p>作为 CNCF 的托管项目，<code>containerd</code> 必须符合 CRI 标准。但是由于很多原因，Docker 只是 <code>containerd</code> 在 Docker Engine 中调用，对外的接口保持不变，也就是说不兼容 CRI。</p> 
<p>由于 Docker 的“固执”，此时 K8s 中有两条调用链：</p> 
<ul><li>使用 CRI 接口调用 <code>dockershim</code>，然后 <code>dockershim</code> 调用 Docker，Docker 再去 <code>containerd</code> 操作容器。</li><li>使用 CRI 接口直接调用 <code>containerd</code> 操作容器。</li></ul> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/d9dd6ee0487943a370f90ea5d30ee680.png" alt="图片"></p> 
<p>显然，因为 <code>containerd</code> 是用来管理容器的，所以这两个调用链的最终效果是完全一样的，但是第二种方法去掉了 <code>dockershim</code> 和 Docker Engine 这两个环节，更加简洁明了，性能也更好。</p> 
<p>2018 年 Kubernetes 1.10 发布时，containerd 也更新到 1.1 版本，正式与 Kubernetes 集成，并发表[博文](https://kubernetes.io/blog/2018/05/24/kubernetes-containerd-integration- gos-ga/ “博文”)显示一些性能测试数据：</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/f5cbdf5f46447c26a4ca11d4070a415d.png" alt="图片"></p> 
<p>从这些数据可以看出，相比当时的 Docker 18.03，<code>containerd1.1</code>Pod 启动延迟降低了 20% 左右，CPU 使用率降低了 68%，内存使用率降低了 12%，这样可观的性能提升对云厂商来说是非常有诱惑力的。</p> 
<h3><a id="Docker_57"></a>弃用Docker</h3> 
<p>2020 年，K8s 1.20 终于正式向 Docker “宣战”：<code>kubelet</code>将弃用 Docker 支持，并将在未来的版本中完全移除。</p> 
<p>但由于 Docker 几乎已经成为容器技术的代名词，而且 K8s 已经使用 Docker 多年，该公告在传播时很快“变味了”，“<code>kubelet</code> 将弃用 Docker 支持”被简化为更吸人眼球的东西 “K8s 将弃用”Docker”。</p> 
<p>这自然引起了 IT 界的恐慌，“不明真相的群众”纷纷表示震惊：</p> 
<p>用了这么久的 Docker 突然不能用了。</p> 
<p>为什么 K8s 会这样对待 Docker？</p> 
<p>之前对 Docker 的投资会归零吗？现有的大量镜像怎么办？</p> 
<p>其实，如果你了解了上面提到的这两个项目<code>CRI</code>，<code>containerd</code>你就会知道，K8s 的这一举动并不奇怪，一切都是“自然”的：其实只是“弃用 <code>dockershim</code> ”，也就是<code>dockershim</code>搬出<code>kubelet</code>，<strong>并不是“弃用 Docker”的软件产品</strong>。</p> 
<p>因此，“弃用 Docker”对 K8s 和 Docker 的影响不大，因为它们都已经将底层改为开源<code>containerd</code>，原有的 Docker 镜像和容器仍然可以正常运行。唯一的变化是K8s绕过了Docker，直接调用Docker内部的<code>containerd</code>。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/17e6fc6be084eb43092ee6193d0bf842.png" alt="图片"></p> 
<p>然而，还是会有一些影响。如果K8s直接使用<code>containerd</code>来操作容器，那么它就是一个独立于Docker的工作环境，两者都无法访问对方管理的容器和镜像。换句话说，使用<code>docker ps</code>命令将不会看到K8s中运行的容器。</p> 
<p>这对一些人来说可能需要花一点时间来适应并使用新工具<code>crictl</code>，但用于查看容器和镜像的子命令仍然是相同的，例如<code>ps</code>,<code>images</code>等，不难适应（如果你一直在用kubectl管理K8s，这个没有影响）。</p> 
<p>K8s 原本计划用一年时间完成“弃用 Docker”的工作，但它确实低估了 Docker 的基础。1.23版本还是没能移除<code>dockershim</code>，只好延期半年。最后，1.24版本从<code>kubelet</code>中删除了<code>dockershim</code>的代码。</p> 
<p>从此，Kubernetes 与 Docker 彻底“分道扬镳”。</p> 
<h3><a id="Docker__87"></a>Docker 的未来</h3> 
<p>那么，Docker 的未来会怎样呢？云原生时代就没有它的立足之地吗？这个问题的答案显然是否定的。</p> 
<p>作为容器技术的奠基人，没有人可以质疑 Docker 的历史地位。虽然 K8s 默认不再绑定 Docker，但 Docker 仍然可以以其他形式的 K8s 共存。</p> 
<p>首先，由于容器镜像格式已经标准化（OCI规范，Open Container Initiative），Docker镜像在K8s中仍然可以正常使用，不需要改变原有的开发测试和CI/CD流程。我们仍然可以拉取 Docker Hub，或者编写一个 Dockerfile 来打包应用程序。</p> 
<p>其次，Docker是一个完整的软件产品线，不仅仅是<code>containerd</code>，它还包括镜像构建、分发、测试等很多服务，甚至连K8s都内置于Docker Desktop中。</p> 
<p>就容器开发的便利性而言，Docker暂时还难以被取代。大多数云原生开发人员可以继续在这个熟悉的环境中工作，使用Docker来开发在K8s中运行的应用程序。</p> 
<p>同样，虽然 K8s 不再包含<code>dockershim</code>，Docker 已经接管了这部分代码并构建了一个名为<code>cri-dockerd</code>的项目，该项目也同样工作，将 Docker Engine 适配为 CRI 接口，这样就<code>kubelet</code>可以通过它再次操作Docker，就好像它从来没有发生过一样。</p> 
<p>总的来说，Docker虽然在容器编排大战中败下阵来，被K8s挤到了墙角，但依然具有很强的生命力。多年积累的众多忠实用户和大量应用形象是其最大的资本和后盾。足以支持它在另一条不与 K8s 正面交锋的道路上。</p> 
<p>对于初学者来说，Docker简单易用，工具链完整，界面友好，市面上很难找到与之相媲美的软件。应该说是入门级学习容器技术和云原生的“最佳选择”。</p>
                </div>
            </body>
            </html>
            